<?php
namespace tp5_lib\menu\logic;

use tp5_lib\install\logic\Module;
use think\Exception;
use tp5_lib\access\logic\PrivilegeLogic;
use tp5_lib\access\logic\RoleLogic;

class Menu extends Module
{
    public function update()
    {
        $result=[];
        $template=require __DIR__ . "/../" . config("module.config_dir") . "menu.template.php";
        $this->scan("menu",function($module,$config)use(&$result,$template){
            if(isset($config["links"])){
                foreach($config["links"] as $index => $link){
                    $config["links"][$index] = array_merge($template["links"]["template"],$link);
                }
            }
            if(isset($config["menus"])){
                foreach($config["menus"] as $index => $link){
                    $config["menus"][$index] = array_merge($template["menus"]["template"],$link);
                }
            }
            unset($config["dir"]);
            $result=array_recursive_merge($result,$config);
        });
        $links=$result["links"];
        foreach($links as $key => $link){
            if(isset($link["type"]) && $link["type"] === "url_path"){
                $link["href"]=url($link["link"]);
            }
            else{
                $link["href"]="";
            }
            $links[$key]=$link;
        }
        
        $menus=$result["menus"];
        foreach($menus as $key => $menu){
            if(isset($result["mapping"][$key])){
                $menus[$key]["sub"]=$result["mapping"][$key];
            }
        }
        $menus=$this->replaceLink($menus,$links);
        $this->write("menu",$menus);
        return true;
    }
    protected function replaceLink($menus,$links)
    {
        foreach($menus as $key => $menu){
            if(is_string($menu)){
                if(isset($links[$menu])){
                    $menu=$links[$menu];
                }
            }
            if(isset($menu["link"])){
                if(isset($links[$menu["link"]])){
                    $menu=array_merge($links[$menu["link"]],$menu);
                }
                unset($menu["link"]);
            }
            if(isset($menu["sub"])){
                $menu["sub"]=$this->replaceLink($menu["sub"],$links);
            }
            $menus[$key]=$menu;
        }
        return $menus;
    }
    /** @var array $menu 当前菜单 */
    protected $menu=[];
    /** 设置菜单名 */
    public function setName($value)
    {
        $menus=$this->read("menu");
        if(!isset($menus[$value])){
            $this->error="目录不存在";
            return false;
        }
        $this->menu=$menus[$value];
        if(isset($this->menu["default"])) $this->setCurrent($this->menu["default"]);
        return $this;
    }
    /**
     * 直接插入菜单数据
     * @todo 添加补全功能
     */
    public function setMenu($value)
    {
        foreach($value["sub"] as $key => $item){
            if(isset($item["type"]) && $item["type"] === "url_path"){
                $item["href"]=url($item["link"]);
            }
            else{
                $item["href"]="";
            }
            $value["sub"][$key]=$item;
        }
        $this->menu=$value;
        if(isset($this->menu["default"])) $this->setCurrent($this->menu["default"]);
        return $this;
    }
    /**
     * @var string $current 当前打开的菜单项
     *  格式为`item1/item2/item3`
     *  以字符串的形式存在，直到读取菜单时再标记出来
     */
    protected $current="";
    /** 设置当前菜单项 */
    public function setCurrent($value)
    {
        if(!$this->checkItem($this->menu,$value)) return false;
        $this->current=$value;
        return $this;
    }
    /** 返回当前菜单项指向的地址 */
    public function getCurrentHref()
    {
        $menu=$this->menu;
        $parts=explode("/",$this->current);
        $sub=$menu["sub"];
        foreach($parts as $part){
            if(isset($sub[$part])){
                $current=$sub[$part];
                if(isset($sub[$part]["sub"])){
                    $sub=$sub[$part]["sub"];
                }
            }
        }
        if(empty($current)) return "";
        return $current["href"];
    }
    /** 获得面包屑导航 */
    public function getBreadCrumb()
    {
        $menu=$this->menu;
        $parts=explode("/",$this->current);
        $list=[];
        $sub=$menu["sub"];
        foreach($parts as $part){
            if(isset($sub[$part])){
                $list[]=$sub[$part];
                if(isset($sub[$part]["sub"])){
                    $sub=$sub[$part]["sub"];
                }
            }
        }
        return $list;
    }
    public function getMenu()
    {
        $menu=$this->menu;
        $menu=$this->filterWithAccess($menu);
        $menu=$this->markItem($menu,$this->current);
        return $menu;
    }
    /** 将指定菜单标记为活动项 */
    protected function markItem($menu,$current)
    {
        $parts=explode("/",$current);
        $sub=&$menu["sub"];
        foreach($parts as $part){
            if(isset($sub[$part])){
                $sub[$part]["active"]=true;
                if(isset($sub[$part]["sub"])){
                    $sub=&$sub[$part]["sub"];
                }
            }
        }
        return $menu;
    }
    /** 检查有没有访问某个菜单项的权限 */
    protected function checkItem($menu,$path)
    {
        $parts=explode("/",$path);
        $privilege=PrivilegeLogic::instance();
        $role=RoleLogic::instance();
        $sub=&$menu["sub"];
        foreach($parts as $part){
            if(isset($sub[$part])){
                $item=$sub[$part];
                if(isset($item["privilege"]) && !$privilege->check($item["privilege"])){
                    return false;
                }
                if(isset($item["role"]) && !$role->check($item["role"])){
                    return false;
                }
                if(isset($sub[$part]["sub"])){
                    $sub=&$sub[$part]["sub"];
                }
            }
        }
        return true;
    }
    /** 过滤没有访问权限的菜单项 */
    protected function filterWithAccess($menu)
    {
        $privilege=PrivilegeLogic::instance();
        $role=RoleLogic::instance();
        return $this->_filterWithAccess($menu,$privilege,$role);
    }
    protected function _filterWithAccess($menu,$privilege,$role)
    {
        foreach($menu["sub"] as $key => $item){
            if(isset($item["privilege"]) && !$privilege->check($item["privilege"])){
                unset($menu["sub"][$key]);
                continue;
            }
            if(isset($item["role"]) && !$role->check($item["role"])){
                unset($menu["sub"][$key]);
                continue;
            }
            if(isset($item["sub"])){
                $menu["sub"][$key]=$this->_filterWithAccess($item,$privilege,$role);
            }
        }
        return $menu;
    }
}
